Struct Vs Class When To Use Which · Quick recall Q&A

2 min read
Mid-level7 min read
Rapid overview

Quick recall Q&A

  • No separate heap allocation
  • No object header
  • No pointer indirection
  • If they are fields of a heap object, they live inside that object
  • If they are boxed (cast to object or interface), they go to the heap
  • Large structs copied often can hurt performance
Q: When should you choose a struct over a class?

When the data is small (≤16 bytes), immutable, frequently created, and benefits from value semantics. Structs reduce GC pressure by living inline and being collected with stack frames. Its data is stored on the stack (for locals), or inline inside another object (as part of that object’s memory). This means

Stack memory is automatically reclaimed when a method returns. No garbage collection is needed for stack-allocated data. This is much cheaper than heap allocation + GC.

public readonly struct Money
{
    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public decimal Amount { get; }
    public string Currency { get; }
}

void Calculate()
{
    Money price = new Money(100, "USD");
    // price is on the stack
} // stack frame is popped → memory reclaimed immediately

⚠️ Structs are not always stack-allocated:

Q: What pitfalls occur when structs are too large?

Copies become expensive, especially when passing by value. This can negate performance gains and increase stack usage. Use in/ref parameters or switch to classes if the struct grows.

Q: How does boxing affect struct performance?

Boxing copies the struct onto the heap and allocates, defeating the GC benefits. Avoid passing structs to APIs expecting object or non-generic interfaces to prevent boxing.

Q: Can structs have parameterless constructors?

Starting with C# 10, yes, but they must be public/private and initialize all fields. Historically, structs always had an implicit default constructor. Remember that every struct has a zeroed default state.

Q: How do you prevent copying when passing structs to methods?

Use in (readonly ref) for read-only access, or ref/ref readonly when you need to mutate or avoid copies. This keeps performance predictable for larger structs.

Q: Can structs inherit from classes?

No. Structs are sealed value types that inherit from ValueType. They can implement interfaces but cannot participate in class inheritance hierarchies.

Q: When do structs hurt cache locality?

Rarely—they often improve locality. However, large structs embedded in arrays can cause cache misses due to size. Evaluate data layout to ensure structs remain lean.

Q: How do you model optional structs?

Use Nullable<T> (Tick?). It wraps the struct with a HasValue flag, allowing null-like semantics without resorting to classes.

Q: What about mutability?

Prefer immutable structs to avoid accidental copies followed by mutation. Mutable structs can lead to confusing bugs when copies diverge silently.

Q: How do structs interact with pattern matching and deconstruction?

They support Deconstruct methods and pattern matching just like classes. This makes them ergonomic for lightweight domain data while still keeping value semantics.


See also